#include "Map.h"
#include "GameConfig.h"
#include "pugixml.hpp"
#include "CNumber.h"
#include "Enemy.h"
#include "EnemyBank.h"
#include "EnemyFactory.h"
#include "MainLayer.h"
#include "xml_node_store.h"
#include "StringUtil.h"
#include "Config.h"
#include "MathUtil.h"


using namespace pugi;

Map::Map(void)
{
	_listEnemy = NULL;
	_listTimeEnemy = NULL;
	_boss = NULL;
	_listEnemyTemplate = NULL;
	_listItemTemplate = NULL;
	_currentTime = 0;
	_scoreTime = 0;
	_isBossNow = false;
	_isBossShow = false;
	_speedUpFactor = 1;
}


Map::~Map(void)
{
	CC_SAFE_RELEASE(_listEnemy);
	CC_SAFE_RELEASE(_listTimeEnemy);
	CC_SAFE_RELEASE(_boss);
	CC_SAFE_RELEASE(_listEnemyTemplate);
	CC_SAFE_RELEASE(_listItemTemplate);
}

bool Map::init()
{
	setEnemies(CCArray::create());
	setTimeEnemies(CCArray::create());
	setEnemyTemplates(CCDictionary::create());
	setItemTemplates(CCDictionary::create());
	// Hard code init
	string data = GameConfig::sharedGameConfig()->getTextFromFile("map1.xml");

	
	xml_document doc;

	xml_parse_result result = doc.load(data.c_str());
	//CCAssert(result.status == status_ok, "FAILD");

	// Enemy Template
	xml_node nEnemiesTemplate = doc.child("Root").child("EnemyTemplate");
	for (xml_node i = nEnemiesTemplate.child("Enemy"); i; i = i.next_sibling("Enemy"))
	{
		
		char* eId = (char*)i.child("Id").text().as_string();
		char* eEnemyId = (char*)i.child("EnemyId").text().as_string();
		int eHealth = i.child("Health").text().as_int();
		int eScore = i.child("Score").text().as_int();
		int velX = i.child("Velocity").child("X").text().as_int();
		int velY = i.child("Velocity").child("Y").text().as_int();
		CCPoint eVelocity = ccp(velX, velY);
		//char* eAiId = (char*)i.child("Ai").child("Id").text().as_string();
		string eAiData = XmlUtil::node_to_string(i.child("Ais"));
		
		CCDictionary* eTeamplate = CCDictionary::create();
		eTeamplate->setObject(ccs(eEnemyId), "EnemyId");
		eTeamplate->setObject(CCNumber<int>::create(eHealth), "Health");
		eTeamplate->setObject(CCNumber<int>::create(eScore), "Score");
		eTeamplate->setObject(CCNumber<CCPoint>::create(eVelocity), "Velocity");
		eTeamplate->setObject(CCNumber<string>::create(eAiData), "AiData");

		_listEnemyTemplate->setObject(eTeamplate, eId);
	}

	// Item Template
	xml_node nItemsTemplate = doc.child("Root").child("ItemTemplate");
	for (xml_node i = nItemsTemplate.child("Item"); i; i = i.next_sibling("Item"))
	{
		char* iId = (char*)i.child("Id").text().as_string();
		char* iItemId = (char*)i.child("ItemId").text().as_string();

		string iData = XmlUtil::node_to_string(i.child("Data"));

		CCDictionary* iTeamplate = CCDictionary::create();
		iTeamplate->setObject(ccs(iItemId), "ItemId");
		iTeamplate->setObject(CCNumber<string>::create(iData), "ItemData");

		_listItemTemplate->setObject(iTeamplate, iId);
	}

	// Map data
	xml_node nEnemies = doc.child("Root").child("Map");
	for (xml_node i = nEnemies.child("Enemy"); i; i = i.next_sibling("Enemy"))
	{
		string sEnemy = (char*)i.text().as_string();
		vector<string> b = StringUtil::split(sEnemy, ' ');
		
		float eTime = StringUtil::toFloat(b.at(0));
		char* eEnemyId = (char*)b.at(1).c_str();
		int ePositionX = StringUtil::toInt(b.at(2));

		CCArray* aItems = CCArray::create();
		for (int j = 3; j < b.size(); j++)
			aItems->addObject(ccs(b.at(j)));

		CCDictionary* data = CCDictionary::create();
		data->setObject(ccs(eEnemyId), "EId");
		data->setObject(CCNumber<int>::create(ePositionX), "PositionX");
		data->setObject(aItems, "Items");

		_listEnemy->addObject(data);
		_listTimeEnemy->addObject(CCNumber<float>::create(eTime));
	}
	

	// Boss now
	xml_node nBoss =  doc.child("Root").child("Map").child("Boss");
	char* bId = (char*)nBoss.child("Id").text().as_string();
	int bHealth = nBoss.child("Health").text().as_int();
	int bScore = nBoss.child("Score").text().as_int();
	int velX = nBoss.child("Velocity").child("X").text().as_int();
	int velY = nBoss.child("Velocity").child("Y").text().as_int();
	CCPoint bVelocity = ccp(velX, velY);
	int posX = nBoss.child("PositionX").text().as_int();

	Enemy* eBos = EnemyBank::sharedEnemyBank()->getEnemyFactory(bId)->createEnemy();
	eBos->setId(ccs(bId));
	eBos->setMaxHealth(bHealth);
	eBos->setScore(bScore);
	eBos->setVelocity(bVelocity);
	eBos->setPosition(ccp(posX, CCEGLView::sharedOpenGLView()->getDesignResolutionSize().height));
	xml_node nAi = nBoss.child("Ais");
	eBos->parseAi(nAi);

	string sItems = nBoss.child("Items").text().as_string();
	vector<string> vItems = StringUtil::split(sItems, ' ');
	CCArray* itemData = CCArray::create(); // List cac item template
	for (int i = 0; i < vItems.size(); i++)
	{
		itemData->addObject(_listItemTemplate->objectForKey(vItems.at(i).c_str()));
	}
	eBos->parseItem(itemData);
	setBoss(eBos);
	
	return true;
}

void Map::onExit()
{
	CCNode::onExit();
}

void Map::processing( float dt )
{
	float gameTime = dt * _speedUpFactor;
	_currentTime += gameTime;
	// update kilomet in MainLayer
	
	if (_scoreTime > TIME_SCORE)
	{
		getMainLayer()->setScore(getMainLayer()->getScore() + 1 * 3);
		_scoreTime = 0;
	}
	else
		_scoreTime += gameTime;

	getMainLayer()->setKilomet(getMainLayer()->getKilomet() + gameTime);

	while (1)
	{
		if (_listTimeEnemy->count() == 0)
			break;
		float time = dynamic_cast<CCNumber<float>*>(_listTimeEnemy->objectAtIndex(0))->getNumber();
		if (time > _currentTime)
			break;
		
		CCDictionary* data = dynamic_cast<CCDictionary*>(_listEnemy->objectAtIndex(0));

		// Generate enemy
		// Get data from <Map>
		CCString* a = dynamic_cast<CCString*>(data->objectForKey("EId"));
		char* id = (char*)a->getCString();
		int posX = dynamic_cast<CCNumber<int>*>(data->objectForKey("PositionX"))->getNumber();
		CCArray* items = dynamic_cast<CCArray*>(data->objectForKey("Items"));

		// 
		CCDictionary* enemyTemplate = dynamic_cast<CCDictionary*>(_listEnemyTemplate->objectForKey(id));
		char* enemyId = (char*)dynamic_cast<CCString*>(enemyTemplate->objectForKey("EnemyId"))->getCString();
		int health = dynamic_cast<CCNumber<int>*>(enemyTemplate->objectForKey("Health"))->getNumber();
		int score = dynamic_cast<CCNumber<int>*>(enemyTemplate->objectForKey("Score"))->getNumber();
		CCPoint vel = dynamic_cast<CCNumber<CCPoint>*>(enemyTemplate->objectForKey("Velocity"))->getNumber();
		string aiData = dynamic_cast<CCNumber<string>*>(enemyTemplate->objectForKey("AiData"))->getNumber();

		
		xml_document doc;
		xml_parse_result result = doc.load(aiData.c_str());
		
		EnemyFactory* eFactory = EnemyBank::sharedEnemyBank()->getEnemyFactory(enemyId);
		Enemy* enemy = eFactory->createEnemy();
		//enemy retain count = 2
		//enemy->setId(ccs(enemyId));
		if (posX == -1)
			posX = MathUtil::randonInt(0, RESOLUTION_WIDTH);
		enemy->setPosition(ccp(posX, CCEGLView::sharedOpenGLView()->getDesignResolutionSize().height));
		enemy->setVelocity(vel);
		enemy->setMaxHealth(health);
		enemy->setScore(score);

		enemy->parseAi(doc.root().child("Ais"));

		CCArray* eItems = CCArray::create();
		CCObject* it;
		CCARRAY_FOREACH(items, it)
		{
			char* dItem = (char*)dynamic_cast<CCString*>(it)->getCString();
			eItems->addObject(_listItemTemplate->objectForKey(dItem));
		}
		enemy->parseItem(eItems);

		getMainLayer()->addEnemy(enemy);
		//enemy retain count = 4
		// Remove from array
		_listTimeEnemy->removeObjectAtIndex(0);
		_listEnemy->removeObjectAtIndex(0);

	}

	// FOR BOSS
	if (_listEnemy->count() == 0 && getMainLayer()->getEnemies()->count() == 0 && !_isBossNow)
	{
		_isBossNow = true;
		float timeDeleySecond = 1.0f;
		runAction(CCSequence::create(CCDelayTime::create(timeDeleySecond), CCCallFuncN::create(this, SEL_CallFuncN(&Map::startBossAnimation)), NULL));
		
	}
}

void Map::startMap()
{
	_currentTime = 0;
	schedule(SEL_SCHEDULE(&Map::processing));
}

MainLayer* Map::getMainLayer()
{
	return dynamic_cast<MainLayer*>(getParent());
}

void Map::startBossAnimation(CCNode* node)
{
	float beginScale = 2.0f;
	float timeScale = 1.0f;
	float endScale = 1.0f;
	float timeDeleyBegin = 1.0f;
	float timeFadeOut = 0.5f;
	

	CCSprite* warning = CCSprite::create("wsparticle_warning.png");
	warning->setPosition(ccp(RESOLUTION_WIDTH/2, RESOLUTION_HEIGHT/2));
	warning->setScale(beginScale);
	getMainLayer()->addChild(warning, 1, WARNING_TAG);
	CCScaleTo* scaleAction = CCScaleTo::create(timeScale, endScale);
	CCFadeOut* fadeOut = CCFadeOut::create(timeFadeOut);

	CCSequence* sequence = CCSequence::create(
		scaleAction, CCDelayTime::create(timeDeleyBegin), fadeOut, CCCallFuncN::create(this, SEL_CallFuncN(&Map::endBossAnimation)), CCCallFuncN::create(this, SEL_CallFuncN(&Map::callBoss)), NULL);

	warning->runAction(sequence);
}

void Map::callBoss( CCNode* node )
{
	getMainLayer()->addEnemy(_boss);
	setBoss(NULL);
}

void Map::endBossAnimation( CCNode* node )
{
	removeChildByTag(WARNING_TAG);
}

bool Map::isMapComplete()
{
	return _isBossNow && !_boss;
}

